
<!DOCTYPE html>
<!--[if IEMobile 7 ]><html class="no-js iem7"><![endif]-->
<!--[if lt IE 9]><html class="no-js lte-ie8"><![endif]-->
<!--[if (gt IE 8)|(gt IEMobile 7)|!(IEMobile)|!(IE)]><!--><html class="no-js"><!--<![endif]-->
<head>
  <meta charset="utf-8">
  <title>
    
  LZH-DEV-TECH-SHARE
  

  </title>
  <meta name="author" content="">
  <meta name="description" content="">

  <meta name="HandheldFriendly" content="True">
  <meta name="MobileOptimized" content="320">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  
  <link href="asset/css/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
  <link href="atom.xml" rel="alternate" title="LZH-DEV-TECH-SHARE" type="application/atom+xml">
  <script src="asset/js/modernizr-2.0.js"></script>
  <script src="asset/js/jquery.min.js"></script>
  <script src="asset/highlightjs/highlight.pack.js"></script>
  <link href="asset/highlightjs/styles/solarized_light.css" media="screen, projection" rel="stylesheet" type="text/css">
  <script>hljs.initHighlightingOnLoad();</script>

  <style type="text/css">
  .cat-children-p{ padding: 6px 0px;}
  .hljs{background: none;}
  </style>
  <script type="text/javascript">
  var isAddSildbar = true;
  </script>
  <script src="asset/js/octopress.js" type="text/javascript"></script>
</head>
<script type="text/javascript">
//链接新开窗口
function addBlankTargetForLinks () {
  $('a[href^="http"]').each(function(){
      $(this).attr('target', '_blank');
  });
}
$(document).ready(function(event) {
  addBlankTargetForLinks();
});
</script>
<body   >
  <header role="banner"><hgroup>
  <h1><a href="index.html">LZH-DEV-TECH-SHARE</a></h1>
  
    <h2></h2>
  
</hgroup>

</header>
  <nav role="navigation"><ul class="subscription" data-subscription="rss">
  <li><a href="atom.xml" rel="subscribe-rss" title="subscribe via RSS">RSS</a></li>
  
</ul>
  
<form action="http://google.com/search" method="get">
  <fieldset role="search">
    <input type="hidden" name="q" value="site:" />
    <input class="search" type="text" name="q" results="0" placeholder="Search"/>
  </fieldset>
</form>
  
<ul class="main-navigation">

  <li id=""><a target="self" href="index.html">Home</a></li>

  <li id=""><a target="_self" href="archives.html">Archives</a></li>

</ul>

</nav>
  <div id="main">
    <div id="content"> 
<div class="blog-index">

	<article>
		 <header>
		  	<h1 class="entry-title"><a href="15413898294420.html">RESTful API 最佳实践</a></h1>
			<p class="meta"><time datetime="2018-11-05T11:50:29+08:00" 
			pubdate data-updated="true">2018/11/5</time></p>
		 </header>
	  	<div class="entry-content">
		  	
		  	<p><a href="http://www.ruanyifeng.com/blog/2011/09/restful.html">RESTful</a> 是目前最流行的 API 设计规范，用于 Web 数据接口的设计。<br/>
它的大原则容易把握，但是细节不容易做对。本文总结 RESTful 的设计细节，介绍如何设计出易于理解和使用的 API。</p>

<h2 id="toc_0">一、URL 设计</h2>

<h3 id="toc_1">1.1 动词 + 宾语</h3>

<p>RESTful 的核心思想就是，客户端发出的数据操作指令都是&quot;动词 + 宾语&quot;的结构。比如，GET /articles这个命令，GET是动词，/articles是宾语。<br/>
动词通常就是五种 HTTP 方法，对应 CRUD 操作。</p>

<pre><code class="language-text">GET：读取（Read）
POST：新建（Create）
PUT：更新（Update）
PATCH：更新（Update），通常是部分更新
DELETE：删除（Delete）
</code></pre>

<p>根据 HTTP 规范，动词一律大写。</p>

<h3 id="toc_2">1.2 动词的覆盖</h3>

<p>有些客户端只能使用GET和POST这两种方法。服务器必须接受POST模拟其他三个方法（PUT、PATCH、DELETE）。<br/>
这时，客户端发出的 HTTP 请求，要加上X-HTTP-Method-Override属性，告诉服务器应该使用哪一个动词，覆盖POST方法。</p>

<pre><code class="language-text">POST /api/Person/4 HTTP/1.1  
X-HTTP-Method-Override: PUT
</code></pre>

<p>上面代码中，X-HTTP-Method-Override指定本次请求的方法是PUT，而不是POST。</p>

<h3 id="toc_3">1.3 宾语必须是名词</h3>

<p>宾语就是 API 的 URL，是 HTTP 动词作用的对象。它应该是名词，不能是动词。比如，/articles这个 URL 就是正确的，而下面的 URL 不是名词，所以都是错误的。</p>

<pre><code class="language-text">/getAllCars
/createNewCar
/deleteAllRedCars
</code></pre>

<h3 id="toc_4">1.4 复数 URL</h3>

<p>既然 URL 是名词，那么应该使用复数，还是单数？<br/>
这没有统一的规定，但是常见的操作是读取一个集合，比如GET /articles（读取所有文章），这里明显应该是复数。<br/>
为了统一起见，建议都使用复数 URL，比如GET /articles/2要好于GET /article/2。</p>

<h3 id="toc_5">1.5 避免多级 URL</h3>

<p>常见的情况是，资源需要多级分类，因此很容易写出多级的 URL，比如获取某个作者的某一类文章。</p>

<pre><code class="language-text">GET /authors/12/categories/2
</code></pre>

<p>这种 URL 不利于扩展，语义也不明确，往往要想一会，才能明白含义。<br/>
更好的做法是，除了第一级，其他级别都用查询字符串表达。</p>

<pre><code class="language-text">GET /authors/12?categories=2
</code></pre>

<p>下面是另一个例子，查询已发布的文章。你可能会设计成下面的 URL。</p>

<pre><code class="language-text">GET /articles/published
</code></pre>

<p>查询字符串的写法明显更好。</p>

<pre><code class="language-text">GET /articles?published=true
</code></pre>

<h2 id="toc_6">二、状态码</h2>

<h3 id="toc_7">2.1 状态码必须精确</h3>

<p>客户端的每一次请求，服务器都必须给出回应。回应包括 HTTP 状态码和数据两部分。<br/>
HTTP 状态码就是一个三位数，分成五个类别。</p>

<pre><code class="language-text">1xx：相关信息
2xx：操作成功
3xx：重定向
4xx：客户端错误
5xx：服务器错误
</code></pre>

<p>这五大类总共包含100多种状态码，覆盖了绝大部分可能遇到的情况。每一种状态码都有标准的（或者约定的）解释，客户端只需查看状态码，就可以判断出发生了什么情况，所以服务器应该返回尽可能精确的状态码。<br/>
API 不需要1xx状态码，下面介绍其他四类状态码的精确含义。</p>

<h3 id="toc_8">2.2 2xx 状态码</h3>

<p>200状态码表示操作成功，但是不同的方法可以返回更精确的状态码。</p>

<pre><code class="language-text">GET: 200 OK
POST: 201 Created
PUT: 200 OK
PATCH: 200 OK
DELETE: 204 No Content
</code></pre>

<p>上面代码中，POST返回201状态码，表示生成了新的资源；DELETE返回204状态码，表示资源已经不存在。<br/>
此外，202 Accepted状态码表示服务器已经收到请求，但还未进行处理，会在未来再处理，通常用于异步操作。下面是一个例子。</p>

<pre><code class="language-javascript">HTTP/1.1 202 Accepted

{
  &quot;task&quot;: {
    &quot;href&quot;: &quot;/api/company/job-management/jobs/2130040&quot;,
    &quot;id&quot;: &quot;2130040&quot;
  }
}
</code></pre>

<h3 id="toc_9">2.3 3xx 状态码</h3>

<p>API 用不到301状态码（永久重定向）和302状态码（暂时重定向，307也是这个含义），因为它们可以由应用级别返回，浏览器会直接跳转，API 级别可以不考虑这两种情况。<br/>
API 用到的3xx状态码，主要是303 See Other，表示参考另一个 URL。它与302和307的含义一样，也是&quot;暂时重定向&quot;，区别在于302和307用于GET请求，而303用于POST、PUT和DELETE请求。收到303以后，浏览器不会自动跳转，而会让用户自己决定下一步怎么办。下面是一个例子。</p>

<pre><code class="language-text">HTTP/1.1 303 See Other
Location: /api/orders/12345
</code></pre>

<h3 id="toc_10">2.4 4xx 状态码</h3>

<p>4xx状态码表示客户端错误，主要有下面几种。<br/>
400 Bad Request：服务器不理解客户端的请求，未做任何处理。<br/>
401 Unauthorized：用户未提供身份验证凭据，或者没有通过身份验证。<br/>
403 Forbidden：用户通过了身份验证，但是不具有访问资源所需的权限。<br/>
404 Not Found：所请求的资源不存在，或不可用。<br/>
405 Method Not Allowed：用户已经通过身份验证，但是所用的 HTTP 方法不在他的权限之内。<br/>
410 Gone：所请求的资源已从这个地址转移，不再可用。<br/>
415 Unsupported Media Type：客户端要求的返回格式不支持。比如，API 只能返回 JSON 格式，但是客户端要求返回 XML 格式。<br/>
422 Unprocessable Entity ：客户端上传的附件无法处理，导致请求失败。<br/>
429 Too Many Requests：客户端的请求次数超过限额。</p>

<h3 id="toc_11">2.5 5xx 状态码</h3>

<p>5xx状态码表示服务端错误。一般来说，API 不会向用户透露服务器的详细信息，所以只要两个状态码就够了。<br/>
500 Internal Server Error：客户端请求有效，服务器处理时发生了意外。<br/>
503 Service Unavailable：服务器无法处理请求，一般用于网站维护状态。</p>

<h2 id="toc_12">三、服务器回应</h2>

<h3 id="toc_13">3.1 不要返回纯本文</h3>

<p>API 返回的数据格式，不应该是纯文本，而应该是一个 JSON 对象，因为这样才能返回标准的结构化数据。所以，服务器回应的 HTTP 头的Content-Type属性要设为application/json。<br/>
客户端请求时，也要明确告诉服务器，可以接受 JSON 格式，即请求的 HTTP 头的ACCEPT属性也要设成application/json。下面是一个例子。</p>

<pre><code class="language-javascript">GET /orders/2 HTTP/1.1 
Accept: application/json
</code></pre>

<h3 id="toc_14">3.2 发生错误时，不要返回 200 状态码</h3>

<p>有一种不恰当的做法是，即使发生错误，也返回200状态码，把错误信息放在数据体里面，就像下面这样。</p>

<pre><code class="language-javascript">HTTP/1.1 200 OK
Content-Type: application/json

{
  &quot;status&quot;: &quot;failure&quot;,
  &quot;data&quot;: {
    &quot;error&quot;: &quot;Expected at least two items in list.&quot;
  }
}
</code></pre>

<p>上面代码中，解析数据体以后，才能得知操作失败。<br/>
这张做法实际上取消了状态码，这是完全不可取的。正确的做法是，状态码反映发生的错误，具体的错误信息放在数据体里面返回。下面是一个例子。</p>

<pre><code class="language-javascript">HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  &quot;error&quot;: &quot;Invalid payoad.&quot;,
  &quot;detail&quot;: {
     &quot;surname&quot;: &quot;This field is required.&quot;
  }
}
</code></pre>

<h3 id="toc_15">3.3 提供链接</h3>

<p>API 的使用者未必知道，URL 是怎么设计的。一个解决方法就是，在回应中，给出相关链接，便于下一步操作。这样的话，用户只要记住一个 URL，就可以发现其他的 URL。这种方法叫做 HATEOAS。<br/>
举例来说，GitHub 的 API 都在 api.github.com 这个域名。访问它，就可以得到其他 URL。</p>

<pre><code class="language-javascript">{
  ...
  &quot;feeds_url&quot;: &quot;https://api.github.com/feeds&quot;,
  &quot;followers_url&quot;: &quot;https://api.github.com/user/followers&quot;,
  &quot;following_url&quot;: &quot;https://api.github.com/user/following{/target}&quot;,
  &quot;gists_url&quot;: &quot;https://api.github.com/gists{/gist_id}&quot;,
  &quot;hub_url&quot;: &quot;https://api.github.com/hub&quot;,
  ...
}
</code></pre>

<p>上面的回应中，挑一个 URL 访问，又可以得到别的 URL。对于用户来说，不需要记住 URL 设计，只要从 api.github.com 一步步查找就可以了。<br/>
HATEOAS 的格式没有统一规定，上面例子中，GitHub 将它们与其他属性放在一起。更好的做法应该是，将相关链接与其他属性分开。</p>

<pre><code class="language-javascript">HTTP/1.1 200 OK
Content-Type: application/json

{
  &quot;status&quot;: &quot;In progress&quot;,
   &quot;links&quot;: {[
    { &quot;rel&quot;:&quot;cancel&quot;, &quot;method&quot;: &quot;delete&quot;, &quot;href&quot;:&quot;/api/status/12345&quot; } ,
    { &quot;rel&quot;:&quot;edit&quot;, &quot;method&quot;: &quot;put&quot;, &quot;href&quot;:&quot;/api/status/12345&quot; }
  ]}
}
</code></pre>

<h2 id="toc_16">四、参考链接</h2>

<p><a href="https://blog.florimondmanca.com/restful-api-design-13-best-practices-to-make-your-users-happy">RESTful API Design: 13 Best Practices to Make Your Users Happy</a>, by Florimond Manca<br/>
<a href="https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design">API design</a>, by MicroSoft Azure</p>


		</div>

		

	</article>
 
	<article>
		 <header>
		  	<h1 class="entry-title"><a href="15411425055347.html">LZH-DEV-GROUP Git Workflow 实践详解</a></h1>
			<p class="meta"><time datetime="2018-11-02T15:08:25+08:00" 
			pubdate data-updated="true">2018/11/2</time></p>
		 </header>
	  	<div class="entry-content">
		  	
		  	<h2 id="toc_0">名词解释</h2>

<ul>
<li>branch</li>
<li>tag</li>
<li>feature</li>
<li>feature-branch</li>
<li>release</li>
<li>release-branch</li>
<li>bug-fix</li>
<li>hot-fix</li>
<li>hot-fix-branch</li>
</ul>


		</div>

		

	</article>
 
	<article>
		 <header>
		  	<h1 class="entry-title"><a href="15410718240579.html">如何使用PM2进程管理器, 守护java进程</a></h1>
			<p class="meta"><time datetime="2018-11-01T19:30:24+08:00" 
			pubdate data-updated="true">2018/11/1</time></p>
		 </header>
	  	<div class="entry-content">
		  	
		  	<blockquote>
<p>java程序的进程管理一直是个业界关注的问题, 以往我们可能使用linux开机启动配置的方式来开机启动java程序, 或者依赖于 runit, supervisor等工具来进行java进程的守护, 今天我们来介绍的是一种更加方便的方式, 它就是PM2</p>
</blockquote>

<h2 id="toc_0">回顾</h2>

<p>前文 [PM2使用详解] 中我们可以看到, 使用pm2, 运行一个node程序及其简单, 命令如下: </p>

<pre><code class="language-shell">pm2 start server.js --name=knowledge -i max 
</code></pre>

<p>可以直接运行 js 伺服程序, 并根据及其的cpu核数, 来创建集群.<br/>
这是因为 pm2 是基于node开发的, 于是可以直接执行 js 伺服程序.</p>

<p>那么如何运行java程序呢, 问题就来了.</p>

<h2 id="toc_1">新知识点</h2>

<h3 id="toc_2">Ecosystem File</h3>

<p>这是用来描述pm2如何部署应用的配置文件, 直译过来, 是<code>生态系统文件</code>.<br/>
我们可以通过:</p>

<pre><code class="language-shell">pm2 init
</code></pre>

<p>来创建这个生态系统文件, 名为<code>ecosystem.config.js</code>, 内容如下:</p>

<pre><code class="language-javascript">module.exports = {
  apps : [{
    name: &quot;app&quot;,
    script: &quot;./app.js&quot;,
    env: {
      NODE_ENV: &quot;development&quot;,
    },
    env_production: {
      NODE_ENV: &quot;production&quot;,
    }
  }]
}
</code></pre>

<p>我们可以看出, 这个配置文件可以支持配置多个应用来运行, 其导出的根是个对象, 对象中apps就是我们要部署的应用们, 每一个对象就是我们要部署的一个应用,单个应用的配置结构类似于:</p>

<pre><code class="language-javascript">{
    name: &quot;app&quot;,
    script: &quot;./app.js&quot;,
    env: {
      NODE_ENV: &quot;development&quot;,
    },
    env_production: {
      NODE_ENV: &quot;production&quot;,
    }
}
</code></pre>

<p>这个对象中的每个选项是一个应用运行的options, 要获取其详细的配置可以<a href="https://pm2.io/doc/en/runtime/reference/ecosystem-file/#apps-options">查看这里</a><br/>
其实pm2 还支持运行 配置文件, 其配置文件格式为 json, 基本格式如图.</p>

<pre><code class="language-javascript">{
    &quot;name&quot;: &quot;my-server&quot;,
    &quot;script&quot;: &quot;/usr/bin/java&quot;,
    &quot;args&quot;: [
        &quot;-jar&quot;,
        &quot;server.jar&quot;
    ],
    &quot;exec_interpreter&quot;: &quot;&quot;,
    &quot;exec_mode&quot;: &quot;fork&quot;
}
</code></pre>

<p>怎么样, 有没感觉上面说的 js 对象的结构和 下面的 json 很像, 没错, 我们可以使用json文件来做单个应用的运行配置描述文件, 其中的属性和前述的 <a href="https://pm2.io/doc/en/runtime/reference/ecosystem-file/#apps-options">单个应用运行的options</a> 是完全一致的, 然后运行单个应用, 这个过程中根本不用考虑前面所说的<code>生态系统文件</code>.</p>

<h3 id="toc_3">注意</h3>

<p>在上述的最后json配置文件例子中, 我们 script 运行了 java 程序, 在 args 中, 指定了运行的jar包, 等信息.</p>

<h3 id="toc_4">小结</h3>

<p>基于此, 我们可以使用 pm2 运行 js, java进程了, 那么其他进程是不是也可以呢, 答案是, 当然可以.<br/>
放开脑洞, 我们可以用pm2运行tomcat, spring-boot应用, node koa应用, node express应用, python进程, go进程, natapp 网络隧道进程等等需要挂起 到后台运行的应用进程, 简直不要太棒!</p>

<h3 id="toc_5">如何启动</h3>

<p>很简单, 只需要:</p>

<pre><code class="language-shell">pm2 start json-config-file.json
</code></pre>

<h2 id="toc_6">结论</h2>

<p>至此, 我们可以很开心的用pm2运行各种程序了, 并基于pm2提供的方便的功能, 查看进程的事实情况, 下一节, 我们将讨论如何利用 pm2的 生态系统文件 -&gt; Ecosystem File 来进行应用的自动更新和部署以及不停机发布.</p>

<h2 id="toc_7">参考资料</h2>

<p><a href="https://www.ddhigh.com/2017/07/31/spring-boot-pm2.html">https://www.ddhigh.com/2017/07/31/spring-boot-pm2.html</a><br/>
<a href="https://github.com/daggerok/pm2-java-spring-boot/blob/master/process.json">https://github.com/daggerok/pm2-java-spring-boot/blob/master/process.json</a><br/>
<a href="https://pm2.io/doc/en/runtime/reference/ecosystem-file/#apps-options">https://pm2.io/doc/en/runtime/reference/ecosystem-file/#apps-options</a></p>


		</div>

		

	</article>
 
	<article>
		 <header>
		  	<h1 class="entry-title"><a href="15408811446857.html">一篇文章搞定 centos7 上的 mysql 安装</a></h1>
			<p class="meta"><time datetime="2018-10-30T14:32:24+08:00" 
			pubdate data-updated="true">2018/10/30</time></p>
		 </header>
	  	<div class="entry-content">
		  	
		  	<ul>
<li>
<a href="#toc_0">下载并安装MySQL官方的 Yum Repository</a>
</li>
<li>
<a href="#toc_1">开始安装MySQL服务器。</a>
</li>
<li>
<a href="#toc_2">MySQL数据库设置</a>
<ul>
<li>
<a href="#toc_3">首先启动MySQL</a>
</li>
<li>
<a href="#toc_4">查看MySQL运行状态，运行状态如图：</a>
</li>
<li>
<a href="#toc_5">进入数据库：</a>
</li>
<li>
<a href="#toc_6">修改初始密码</a>
</li>
<li>
<a href="#toc_7">避免mysql自动更新</a>
</li>
<li>
<a href="#toc_8">mysql root 用户远程访问支持</a>
<ul>
<li>
<a href="#toc_9">登陆mysql</a>
</li>
<li>
<a href="#toc_10">查看 user 表数据</a>
</li>
<li>
<a href="#toc_11">实现远程连接(改表法)</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>


<blockquote>
<p>在CentOS中默认安装有MariaDB，这个是MySQL的分支，但为了需要，还是要在系统中安装MySQL，而且安装完成之后可以直接覆盖掉MariaDB。</p>
</blockquote>

<h2 id="toc_0">下载并安装MySQL官方的 Yum Repository</h2>

<pre><code class="language-shell">[root@localhost ~]# wget -i -c http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm
</code></pre>

<p>使用上面的命令就直接下载了安装用的Yum Repository，大概25KB的样子，然后就可以直接yum安装了。</p>

<pre><code class="language-shell">[root@localhost ~]# yum -y install mysql57-community-release-el7-10.noarch.rpm
</code></pre>

<h2 id="toc_1">开始安装MySQL服务器。</h2>

<pre><code class="language-shell">[root@localhost ~]# yum -y install mysql-community-server
</code></pre>

<p>这步可能会花些时间，安装完成后就会覆盖掉之前的mariadb。<br/>
<img src="media/15408811446857/15408813515173.jpg" alt=""/></p>

<p>至此MySQL就安装完成了，然后是对MySQL的一些设置。</p>

<h2 id="toc_2">MySQL数据库设置</h2>

<h3 id="toc_3">首先启动MySQL</h3>

<pre><code class="language-shell">[root@localhost ~]# systemctl start  mysqld.service
</code></pre>

<h3 id="toc_4">查看MySQL运行状态，运行状态如图：</h3>

<pre><code class="language-shell">[root@localhost ~]# systemctl status mysqld.service
</code></pre>

<p><img src="media/15408811446857/15408813962176.jpg" alt=""/></p>

<p>此时MySQL已经开始正常运行，不过要想进入MySQL还得先找出此时root用户的密码，通过如下命令可以在日志文件中找出密码：</p>

<p>[root@localhost ~]# grep &quot;password&quot; /var/log/mysqld.log<br/>
<img src="media/15408811446857/15408814188529.jpg" alt=""/></p>

<h3 id="toc_5">进入数据库：</h3>

<pre><code class="language-shell">[root@localhost ~]# mysql -uroot -p
</code></pre>

<p>输入初始密码，此时不能做任何事情，因为MySQL默认必须修改密码之后才能操作数据库：</p>

<h3 id="toc_6">修改初始密码</h3>

<pre><code class="language-shell">mysql&gt; ALTER USER &#39;root&#39;@&#39;localhost&#39; IDENTIFIED BY &#39;new password&#39;;
</code></pre>

<p>这里有个问题，新密码设置的时候如果设置的过于简单会报错：<br/>
<img src="media/15408811446857/15408814703369.jpg" alt=""/></p>

<p>原因是因为MySQL有密码设置的规范，具体是与validate_password_policy的值有关：<br/>
 <img src="media/15408811446857/15408815243747.jpg" alt=""/></p>

<p>MySQL完整的初始密码规则可以通过如下命令查看：</p>

<pre><code class="language-mysql">mysql&gt; SHOW VARIABLES LIKE &#39;validate_password%&#39;;
+--------------------------------------+-------+
| Variable_name                        | Value |
+--------------------------------------+-------+
| validate_password_check_user_name    | OFF   |
| validate_password_dictionary_file    |       |
| validate_password_length             | 4     |
| validate_password_mixed_case_count   | 1     |
| validate_password_number_count       | 1     |
| validate_password_policy             | LOW   |
| validate_password_special_char_count | 1     |
+--------------------------------------+-------+
7 rows in set (0.01 sec)
</code></pre>

<p>密码的长度是由validate_password_length决定的，而validate_password_length的计算公式是：</p>

<pre><code class="language-shell">validate_password_length = validate_password_number_count + validate_password_special_char_count + (2 * validate_password_mixed_case_count)
</code></pre>

<p>我的是已经修改过的，初始情况下第一个的值是ON，validate_password_length是8。可以通过如下命令修改：</p>

<pre><code class="language-shell">mysql&gt; set global validate_password_policy=0;
mysql&gt; set global validate_password_length=1;
</code></pre>

<p>设置之后就是我上面查出来的那几个值了，此时密码就可以设置的很简单，例如1234之类的。到此数据库的密码设置就完成了。</p>

<h3 id="toc_7">避免mysql自动更新</h3>

<p>但此时还有一个问题，就是因为安装了Yum Repository，以后每次yum操作都会自动更新，需要把这个卸载掉：</p>

<pre><code class="language-shell">[root@localhost ~]# yum -y remove mysql57-community-release-el7-10.noarch
</code></pre>

<p>此时才算真的完成了。</p>

<h3 id="toc_8">mysql root 用户远程访问支持</h3>

<h4 id="toc_9">登陆mysql</h4>

<pre><code class="language-shell">mysql -u root -p
</code></pre>

<h4 id="toc_10">查看 user 表数据</h4>

<pre><code class="language-shell">mysql&gt; use mysql;
Database changed
mysql&gt; select host,user from user;
+-----------+---------------+
| host      | user          |
+-----------+---------------+
| localhost | mysql.session |
| localhost | mysql.sys     |
| localhost | root          |
+-----------+---------------+
3 rows in set (0.00 sec)
</code></pre>

<h4 id="toc_11">实现远程连接(改表法)</h4>

<pre><code class="language-shell">update user set host = &#39;%&#39; where user = &#39;root&#39;;
</code></pre>

<p>这样在远端就可以通过root用户访问Mysql.</p>


		</div>

		

	</article>
 
	<article>
		 <header>
		  	<h1 class="entry-title"><a href="15408703788547.html">一篇文章搞定 centos7 下安装 nvm和node</a></h1>
			<p class="meta"><time datetime="2018-10-30T11:32:58+08:00" 
			pubdate data-updated="true">2018/10/30</time></p>
		 </header>
	  	<div class="entry-content">
		  	
		  	<ul>
<li>
<a href="#toc_0">在github中搜索nvm</a>
</li>
<li>
<a href="#toc_1">进入项目</a>
</li>
<li>
<a href="#toc_2">安装nvm</a>
<ul>
<li>
<a href="#toc_3">配置环境变量</a>
</li>
</ul>
</li>
<li>
<a href="#toc_4">使用 nvm 安装 node</a>
<ul>
<li>
<ul>
<li>
<a href="#toc_5">查询nvm管理的本地node版本</a>
</li>
<li>
<a href="#toc_6">查询远程可供安装的nvm管理的node版本</a>
</li>
<li>
<a href="#toc_7">安装最新的LTS版本</a>
</li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#toc_8">安装完成</a>
</li>
</ul>


<h2 id="toc_0">在github中搜索nvm</h2>

<p><img src="media/15408703788547/15408704430866.jpg" alt="" style="width:730px;"/></p>

<h2 id="toc_1">进入项目</h2>

<p>在项目的readme.md中, 就有很详细的安装过程了.<br/>
<img src="media/15408703788547/15408705766395.jpg" alt="" style="width:943px;"/></p>

<h2 id="toc_2">安装nvm</h2>

<pre><code class="language-shell">curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
</code></pre>

<h3 id="toc_3">配置环境变量</h3>

<p>将如下代码加入到你的shell配置文件中(一般这些文件是:<br/>
~/.bash_profile, ~/.zshrc, ~/.profile, or ~/.bashrc)</p>

<pre><code class="language-shell">export NVM_DIR=&quot;${XDG_CONFIG_HOME/:-$HOME/.}nvm&quot;
[ -s &quot;$NVM_DIR/nvm.sh&quot; ] &amp;&amp; \. &quot;$NVM_DIR/nvm.sh&quot; # This loads nvm
</code></pre>

<h2 id="toc_4">使用 nvm 安装 node</h2>

<p>这就很简单了我们先</p>

<h4 id="toc_5">查询nvm管理的本地node版本</h4>

<pre><code class="language-shell">nvm list
</code></pre>

<p><img src="media/15408703788547/15408715171278.jpg" alt=""/><br/>
发现什么都没装</p>

<h4 id="toc_6">查询远程可供安装的nvm管理的node版本</h4>

<pre><code class="language-shell">nvm ls-remote
</code></pre>

<p><img src="media/15408703788547/15408716254036.jpg" alt=""/><br/>
结果我们发现好大一坨, 没关系, 我们只要选用最新的LTS版本即可<br/>
这里我们选用v8.12.0</p>

<h4 id="toc_7">安装最新的LTS版本</h4>

<pre><code class="language-shell">nvm install v8.12.0
</code></pre>

<p><img src="media/15408703788547/15408717009392.jpg" alt=""/></p>

<h2 id="toc_8">安装完成</h2>

<p>至此安装已经结束了, 在命令行输入node, 尝试一下最新的LTS版本的node吧.</p>


		</div>

		

	</article>
 
	<article>
		 <header>
		  	<h1 class="entry-title"><a href="15408695578947.html">一篇文章搞定 Centos7 安装与配置JDK8</a></h1>
			<p class="meta"><time datetime="2018-10-30T11:19:17+08:00" 
			pubdate data-updated="true">2018/10/30</time></p>
		 </header>
	  	<div class="entry-content">
		  	
		  	<ul>
<li>
<a href="#toc_0">检查是否安装过JDK</a>
</li>
<li>
<a href="#toc_1">下载安装包并上传</a>
</li>
<li>
<a href="#toc_2">使用rpm的方式安装JDK</a>
</li>
<li>
<a href="#toc_3">安装完成后的检查</a>
</li>
<li>
<a href="#toc_4">配置环境变量</a>
</li>
<li>
<a href="#toc_5">安装结束</a>
</li>
</ul>


<h2 id="toc_0">检查是否安装过JDK</h2>

<pre><code class="language-shell">rpm -qa | grep java
</code></pre>

<h2 id="toc_1">下载安装包并上传</h2>

<p>注意, 这里一定选择在官网下载, 并且选择下载rpm包如图<br/>
<img src="media/15408695578947/15408696761847.jpg" alt="" style="width:562px;"/><br/>
下载好后, 上传到服务器</p>

<h2 id="toc_2">使用rpm的方式安装JDK</h2>

<pre><code class="language-shell">rpm -ivh jdk-8u191-linux-x64.rpm
</code></pre>

<h2 id="toc_3">安装完成后的检查</h2>

<p>通过上一步骤, 我们已经安装好了java, 我们可以使用如下命令检查安装结果</p>

<pre><code class="language-shell">java -version
</code></pre>

<h2 id="toc_4">配置环境变量</h2>

<pre><code class="language-shell">vim /etc/profile

JAVA_HOME=/usr/java/jdk1.8.0_191-amd64
JRE_HOME=/usr/java/jdk1.8.0_191-amd64/jre
PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
export JAVA_HOME JRE_HOME PATH CLASSPATH
</code></pre>

<h2 id="toc_5">安装结束</h2>

<p>至此安装结束, 需要注意的是, 在最后你可能需要重启shell, 或者source shell 配置文件来使环境变量配置生效, 具体如下:</p>

<pre><code class="language-shell">source /etc/profile
</code></pre>


		</div>

		

	</article>
 
	<article>
		 <header>
		  	<h1 class="entry-title"><a href="15408688513918.html">一篇文章搞定centos7 oh-my-zsh安装</a></h1>
			<p class="meta"><time datetime="2018-10-30T11:07:31+08:00" 
			pubdate data-updated="true">2018/10/30</time></p>
		 </header>
	  	<div class="entry-content">
		  	
		  	<ul>
<li>
<a href="#toc_0">安装zsh</a>
</li>
<li>
<a href="#toc_1">设置zsh为默认shell</a>
</li>
<li>
<a href="#toc_2">安装 oh-my-zsh</a>
</li>
<li>
<a href="#toc_3">其他</a>
</li>
</ul>


<h2 id="toc_0">安装zsh</h2>

<pre><code class="language-shell">sudo yum install zsh
</code></pre>

<h2 id="toc_1">设置zsh为默认shell</h2>

<pre><code class="language-shell">sudo chsh -s $(which zsh)
</code></pre>

<h2 id="toc_2">安装 oh-my-zsh</h2>

<pre><code class="language-shell">sh -c &quot;$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)&quot;
</code></pre>

<h2 id="toc_3">其他</h2>

<p>其他操作系统请灵活变通, 例如 ubuntu 你可能需要使用 apt 来装zsh, mac 你可能需要 brew 来安装最新版 zsh.</p>


		</div>

		

	</article>
 
	<article>
		 <header>
		  	<h1 class="entry-title"><a href="15404694150903.html">mac终端下svn常用命令</a></h1>
			<p class="meta"><time datetime="2018-10-25T20:10:15+08:00" 
			pubdate data-updated="true">2018/10/25</time></p>
		 </header>
	  	<div class="entry-content">
		  	
		  	<ul>
<li>
<a href="#toc_0">将文件checkout到本地目录</a>
</li>
<li>
<a href="#toc_1">往版本库中添加新的文件</a>
</li>
<li>
<a href="#toc_2">将改动的文件提交到版本库</a>
</li>
<li>
<a href="#toc_3">加锁/解锁</a>
</li>
<li>
<a href="#toc_4">更新到某个版本</a>
</li>
<li>
<a href="#toc_5">查看文件或者目录状态</a>
</li>
<li>
<a href="#toc_6">删除文件</a>
</li>
<li>
<a href="#toc_7">查看日志</a>
</li>
<li>
<a href="#toc_8">查看文件详细信息</a>
</li>
<li>
<a href="#toc_9">比较差异</a>
</li>
<li>
<a href="#toc_10">将两个版本之间的差异合并到当前文件</a>
</li>
<li>
<a href="#toc_11">SVN 帮助</a>
</li>
<li>
<a href="#toc_12">版本库下的文件和目录列表</a>
</li>
<li>
<a href="#toc_13">创建纳入版本控制下的新目录</a>
</li>
<li>
<a href="#toc_14">恢复本地修改</a>
</li>
<li>
<a href="#toc_15">代码库URL变更</a>
</li>
<li>
<a href="#toc_16">解决冲突</a>
</li>
</ul>


<p>以下是一些常用命令</p>

<h3 id="toc_0">将文件checkout到本地目录</h3>

<pre><code class="language-shell">$ svn checkout path（path是服务器上的目录）
例如：
$ svn checkout svn://192.168.1.1/pro/domain
简写：
$ svn co
</code></pre>

<h3 id="toc_1">往版本库中添加新的文件</h3>

<pre><code class="language-shell">$ svn add file
例如：
$ svn add test.php(添加test.php)
$ svn add *.php(添加当前目录下所有的php文件)
</code></pre>

<h3 id="toc_2">将改动的文件提交到版本库</h3>

<pre><code class="language-shell">$ svn commit -m “LogMessage“ [-N] [--no-unlock] PATH　　　　　　　　(如果选择了保持锁，就使用–no-unlock开关)
例如：
$ svn commit -m “add test file for my test“ test.php
简写：
$ svn ci
</code></pre>

<h3 id="toc_3">加锁/解锁</h3>

<pre><code class="language-shell">$ svn lock -m “LockMessage“ [--force] PATH
例如：
$ svn lock -m “lock test file“ test.php
$ svn unlock PATH
</code></pre>

<h3 id="toc_4">更新到某个版本</h3>

<pre><code class="language-shell">$ svn update -r m path
例如：
$ svn update 如果后面没有目录，默认将当前目录以及子目录下的所有文件都更新到最新版本。
$ svn update -r 200 test.php (将版本库中的文件test.php还原到版本200)
$ svn update test.php (更新，于版本库同步。如果在提交的时候提示过期的话，是因为冲突，需要先update，修改文件，然后清除svn resolved，最后再提交commit)
简写：
$ svn up
</code></pre>

<h3 id="toc_5">查看文件或者目录状态</h3>

<pre><code class="language-shell">$ svn status path
（目录下的文件和子目录的状态，正常状态不显示）
【?：不在svn的控制中；M：内容被修改；C：发生冲突；A：预定加入到版本库；K：被锁定】
$ svn status -v path
 (显示文件和子目录状态)
第一列保持相同，第二列显示工作版本号，第三和第四列显示最后一次修改的版本号和修改人。
注：
$ svn status
$ svn diff
$ svn revert
这三条命令在没有网络的情况下也可以执行的，原因是svn在本地的.svn中保留了本地版本的原始拷贝。
简写：
$ svn st
</code></pre>

<h3 id="toc_6">删除文件</h3>

<pre><code class="language-shell">$ svn delete path -m “delete test fle“
例如：
$ svn delete svn://192.168.1.1/pro/domain/test.php -m “delete test file”
或者直接
$ svn delete test.php 
然后再 
$ svn ci -m ‘delete test file‘，推荐使用这种
简写：
$ svn (del, remove, rm)
</code></pre>

<h3 id="toc_7">查看日志</h3>

<pre><code class="language-shell">$ svn log path
例如：
$ svn log test.php 显示这个文件的所有修改记录，及其版本号的变化
</code></pre>

<h3 id="toc_8">查看文件详细信息</h3>

<pre><code class="language-shell">$ svn info path
例如：
$ svn info test.php
</code></pre>

<h3 id="toc_9">比较差异</h3>

<pre><code class="language-shell">$ svn diff path(将修改的文件与基础版本比较)
例如：
$ svn diff test.php
$ svn diff -r m:n path(对版本m和版本n比较差异)
例如：
$ svn diff -r 200:201 test.php
简写：
$ svn di
</code></pre>

<h3 id="toc_10">将两个版本之间的差异合并到当前文件</h3>

<pre><code class="language-shell">$ svn merge -r m:n path
例如：
$ svn merge -r 200:205 test.php（将版本200与205之间的差异合并到当前文件，但是一般都会产生冲突，需要处理一下）
</code></pre>

<h3 id="toc_11">SVN 帮助</h3>

<pre><code class="language-shell">$ svn help
$ svn help ci
</code></pre>

<h3 id="toc_12">版本库下的文件和目录列表</h3>

<pre><code class="language-shell">$ svn list path
显示path目录下的所有属于版本库的文件和目录
简写：
$ svn ls
</code></pre>

<h3 id="toc_13">创建纳入版本控制下的新目录</h3>

<pre><code class="language-shell">$ svn mkdir: 创建纳入版本控制下的新目录。
用法: 
$ svn mkdir PATH
$ svn mkdir URL
创建版本控制的目录。
1. 每一个以工作副本 PATH 指定的目录，都会创建在本地端，并且加入新增
调度，以待下一次的提交。
2. 每个以URL指定的目录，都会透过立即提交于仓库中创建。
在这两个情况下，所有的中间目录都必须事先存在
</code></pre>

<h3 id="toc_14">恢复本地修改</h3>

<pre><code class="language-shell">$ svn revert: 恢复原始未改变的工作副本文件 (恢复大部份的本地修改)。
revert:
用法: 
revert PATH
注意: 本子命令不会存取网络，并且会解除冲突的状况。但是它不会恢复被删除的目录
</code></pre>

<h3 id="toc_15">代码库URL变更</h3>

<pre><code class="language-shell"> $ svn switch (sw): 更新工作副本至不同的URL。
 用法: 
 switch URL [PATH]
 switch –relocate FROM TO [PATH...]
   
1、更新你的工作副本，映射到一个新的URL，其行为跟“svn update”很像，也会将服务器上文件与本地文件合并。这是将工作副本对应到同一仓库中某个分支或者标记的方法。
2、改写工作副本的URL元数据，以反映单纯的URL上的改变。当仓库的根URL变动(比如方案名或是主机名称变动)，但是工作副本仍旧对映到同一仓库的同一目录时使用这个命令更新工作副本与仓库的对应关系。
</code></pre>

<h3 id="toc_16">解决冲突</h3>

<pre><code class="language-shell">$ svn resolved: 移除工作副本的目录或文件的“冲突”状态。
用法: resolved PATH…
注意: 本子命令不会依语法来解决冲突或是移除冲突标记；它只是移除冲突的相关文件，然后让 PATH 可以再次提交。
</code></pre>


		</div>

		

	</article>
 
	<article>
		 <header>
		  	<h1 class="entry-title"><a href="15403770118414.html">Git Workflow 工作指南</a></h1>
			<p class="meta"><time datetime="2018-10-24T18:30:11+08:00" 
			pubdate data-updated="true">2018/10/24</time></p>
		 </header>
	  	<div class="entry-content">
		  	
		  	<ul>
<li>
<a href="#toc_0">分支策略</a>
</li>
<li>
<a href="#toc_1">常用分支</a>
<ul>
<li>
<a href="#toc_2">master: 正式发布</a>
</li>
<li>
<a href="#toc_3">develop: 日常开发</a>
</li>
</ul>
</li>
<li>
<a href="#toc_4">临时分支</a>
<ul>
<li>
<a href="#toc_5">功能分支</a>
</li>
<li>
<a href="#toc_6">发布分支</a>
</li>
<li>
<a href="#toc_7">修补分支</a>
</li>
</ul>
</li>
<li>
<a href="#toc_8">Pull / Merge Request</a>
</li>
<li>
<a href="#toc_9">Code Review</a>
</li>
<li>
<a href="#toc_10">Git Command</a>
</li>
<li>
<a href="#toc_11">参考</a>
</li>
</ul>


<h2 id="toc_0">分支策略</h2>

<ul>
<li>代码库的常设分支始终只有 master 和 develop，临时分支最后都删除。</li>
<li>临时分支合并到常用分支时，必须发起 Pull / Merge Requset ，并指定一个人 code review。</li>
<li>远程临时分支由发起者追踪和维护， reviewer 负责删除。</li>
<li>所有的开发和迭代尽量都在临时分支上。</li>
<li>开发记录、功能集成、测试历史由 develop 分支管理，正式发布记录由 master 分支管理。</li>
<li>发布和部署时，必须新建发布分支。（发布分支基于 develop 分支）</li>
<li>发布和部署完成后，必须将发布分支合并回 develop / master 分支，然后删除发布分支。</li>
<li>合并到 master 分支的代码必须打上 Tag。（Tag：版本号、描述、日期）</li>
</ul>

<h2 id="toc_1">常用分支</h2>

<ul>
<li>master</li>
<li>develop</li>
</ul>

<h3 id="toc_2">master: 正式发布</h3>

<p>master 分支上存放的应该是随时可供在生产环境中部署的代码（Production Ready state）。当开发活动告一段落，产生了一份新的可供部署的代码时，master 分支上的代码会被更新。同时，每一次更新，需要添加对应的版本号标签（TAG）。</p>

<h3 id="toc_3">develop: 日常开发</h3>

<p>develop 分支是保存当前最新开发成果的分支。通常这个分支上的代码也是可进行每日夜间发布的代码（Nightly build）。因此这个分支有时也可以被称作“integration branch”。</p>

<h2 id="toc_4">临时分支</h2>

<ul>
<li>功能分支（feature）</li>
<li>发布分支（release）</li>
<li>修补分支（hotfix）</li>
</ul>

<h3 id="toc_5">功能分支</h3>

<p>功能分支，开发新功能都从 develop 分支出来，从 develop 分支上面分出来的。开发完成后，要再并入 develop。</p>

<pre><code class="language-shell"># 创建一个功能分支：
git checkout -b feature-x develop

# 开发完成后，将功能分支合并到develop分支：
git checkout develop
git merge --no-ff feature-x

# 删除feature 分支：
git branch -d feature-x
</code></pre>

<h3 id="toc_6">发布分支</h3>

<p>发布分支，准备要 release 的版本，只修 bugs。从 develop 分支上面分出来，发布结束以后，必须合并进 develop 和 master 分支。</p>

<pre><code class="language-shell"># 创建一个发布分支：
git checkout -b release-1.2 develop

# 确认没有问题后，合并到master分支：
git checkout master
git merge --no-ff release-1.2

# 对合并生成的新节点，做一个标签
git tag -a 1.2

# 再合并到develop分支：
git checkout develop
git merge --no-ff release-1.2

# 最后，删除发布分支：
git branch -d release-1.2
</code></pre>

<h3 id="toc_7">修补分支</h3>

<p>修补分支，是指等不及 release 版本就必须马上修 master 赶上线的情况。它是从 master 分支上面分出来的。修补结束以后， 再合并进 master 和 develop 分支。</p>

<pre><code class="language-shell"># 创建一个修补bug分支：
git checkout -b hotfix-0.1 master

# 修补结束后，合并到master分支：
git checkout master
git merge --no-ff hotfix-0.1
git tag -a 0.1.1

# 再合并到develop分支：
git checkout develop
git merge --no-ff hotfix-0.1

# 最后，删除&quot;修补bug分支&quot;：
git branch -d hotfix-0.1
</code></pre>

<h2 id="toc_8">Pull / Merge Request</h2>

<p>代码合并到 master/develop 分支：</p>

<ol>
<li>将需要合并到 master / develop 的分支 push 到 gitlab。</li>
<li>进入工程 -&gt; merge request -&gt; create new merge request 。</li>
<li>选择源分支、目标分支，确定。</li>
<li>review 负责人进入 merge request，确认没有问题之后选择 Auto Merge（或者手动在本地合并之后再 push 到 gitlab），并关闭这个 merge request，完成。</li>
<li>如果发现问题那么在有问题的行下注释，并提醒 request 的发起人及时修改。</li>
<li>删除本地临时分支，本地 master / develop 更新到最新状态。</li>
</ol>

<h2 id="toc_9">Code Review</h2>

<ul>
<li>提交 Pull / Merge Request 时， Commit 和 Message 要足够清晰详细。 切记，如果一次提交的内容包含很多 Commit，请不要使用自动生成的描述。 请用简短且足够说明问题的语言（理想是控制在3句话之内）来描述：</li>
</ul>

<blockquote>
<p>你改动了什么，解决了什么问题，需要代码审查的人留意那些影响比较大的改动。特别需要留意，如果对基础、公共的组件进行了改动，一定要另起一行特别说明。</p>
</blockquote>

<ul>
<li>审核人员邀请原则：项目参与人员 &amp; 团队同事 &amp; 团队 Leader。（对项目足够了解，对项目足够了解，对项目足够了解，重要的事情说三遍）；</li>
<li>评论中至少出现一个 lgtm 且保证代码评审通过之后 Pull / Merge Request 才可以被合并；（注：lgtm 即 looks good to me 的缩写）</li>
</ul>

<h2 id="toc_10">Git Command</h2>

<pre><code class="language-shell">git tag
# 创建一个带版本有描述的 tag
git tag -a v0.1.0 -m &#39;commit&#39;
# 覆盖该版本已有 v0.1.0 
git tag -f v0.1.0 
# 推送服务器
git push origin --tags
# 服务器已有 v0.1.0，强制推到服务器
git push origin -f v0.1.0 
# 服务器获取刚刚的 v0.1.0
git fetch --tag
# 删除本地版本
git tag -d v0.1.0
# 删除服务器上的tag
git push origin :v0.1.0
git merge

# 不使用 fast-forward 方式合并，保留分支的 commit 历史
git merge --no-ff develop
# 使用 squash 方式合并，把多次分支commit历史压缩为一次
git merge --squash develop
git checkout

# 放弃在 file.txt 的修改，恢复成未修改时的样子
git checkout file.txt
# 当前目录所有修改的文件，恢复成未修改时的样子
git checkout .
# 创建新的分支，并切换过去
git checkout -b branchname
git reset

# 回退指定的 commit 
git reset 0c5602affd27d2224d151284bd1c6e033fd9023f
# 回退上次修改
git reset --hard
git flow

# git flow 初始化
git flow init
# 创建一个新的 feature 分支
git flow feature start name
# 将 feature 分支推送到远程仓库
git flow feature publish name
# 当特性开发完毕，需要将此分支合并到 develop 分支
git flow feature finish name
</code></pre>

<h2 id="toc_11">参考</h2>

<ol>
<li><a href="https://segmentfault.com/http://www.ruanyifeng.com/blog/2012/07/git.html/">Git分支管理策略</a></li>
<li><a href="https://segmentfault.com/http://www.15yan.com/topic/yi-dong-kai-fa-na-dian-shi/6yueHxcgD9Z/">GitLab Flow的使用</a></li>
<li><a href="https://segmentfault.com/https://blog.zengrong.net/post/1746.html/">Git查看、删除、重命名远程分支和tag</a></li>
<li><a href="https://segmentfault.com/http://blog.jobbole.com/76867/">Git工作流指南：Gitflow工作流</a></li>
<li><a href="Git%20%E5%9C%A8%E5%9B%A2%E9%98%9F%E4%B8%AD%E7%9A%84%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5--%E5%A6%82%E4%BD%95%E6%AD%A3%E7%A1%AE%E4%BD%BF%E7%94%A8Git%20Flow">Git 在团队中的最佳实践--如何正确使用Git Flow</a></li>
</ol>


		</div>

		

	</article>
 
	<article>
		 <header>
		  	<h1 class="entry-title"><a href="15402950858118.html">Nginx的启动、停止与重启</a></h1>
			<p class="meta"><time datetime="2018-10-23T19:44:45+08:00" 
			pubdate data-updated="true">2018/10/23</time></p>
		 </header>
	  	<div class="entry-content">
		  	
		  	<ul>
<li>
<a href="#toc_0">启动</a>
</li>
<li>
<a href="#toc_1">停止</a>
<ul>
<li>
<a href="#toc_2">从容停止</a>
</li>
<li>
<a href="#toc_3">快速停止</a>
</li>
<li>
<a href="#toc_4">强制停止</a>
</li>
</ul>
</li>
<li>
<a href="#toc_5">重启</a>
</li>
</ul>


<h2 id="toc_0">启动</h2>

<p>启动代码格式：nginx安装目录地址 -c nginx配置文件地址</p>

<p>例如：</p>

<pre><code class="language-shell">[root@LinuxServer sbin]# /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
</code></pre>

<h2 id="toc_1">停止</h2>

<p>nginx的停止有三种方式：</p>

<h3 id="toc_2">从容停止</h3>

<p>　　1、查看进程号</p>

<pre><code class="language-shell">[root@LinuxServer ~]# ps -ef|grep nginx
</code></pre>

<p><img src="media/15402950858118/15402954030895.jpg" alt=""/></p>

<p>　　2、杀死进程</p>

<pre><code class="language-shell">[root@LinuxServer ~]# kill -QUIT 2072
</code></pre>

<p><img src="media/15402950858118/15402954134920.jpg" alt=""/></p>

<h3 id="toc_3">快速停止</h3>

<p>1、查看进程号</p>

<pre><code class="language-shell">[root@LinuxServer ~]# ps -ef|grep nginx
</code></pre>

<p><img src="media/15402950858118/15402954229262.jpg" alt=""/></p>

<p>2、杀死进程</p>

<pre><code class="language-shell">[root@LinuxServer ~]# kill -TERM 2132
</code></pre>

<p>或 </p>

<pre><code class="language-shell">[root@LinuxServer ~]# kill -INT 2132
</code></pre>

<p><img src="media/15402950858118/15402954314580.jpg" alt=""/></p>

<h3 id="toc_4">强制停止</h3>

<pre><code class="language-shell">[root@LinuxServer ~]# pkill -9 nginx
</code></pre>

<h2 id="toc_5">重启</h2>

<ul>
<li>验证nginx配置文件是否正确</li>
</ul>

<p>方法一：进入nginx安装目录sbin下，输入命令</p>

<pre><code class="language-shell">./nginx -t
</code></pre>

<p>看到如下显示</p>

<pre><code class="language-shell">nginx.conf syntax is ok

nginx.conf test is successful
</code></pre>

<p>说明配置文件正确！</p>

<p><img src="media/15402950858118/15402954436521.jpg" alt=""/></p>

<p>方法二：在启动命令-c前加-t<br/>
<img src="media/15402950858118/15402954542875.jpg" alt=""/></p>

<ul>
<li><p>重启Nginx服务<br/>
方法一：进入nginx可执行目录sbin下，输入命令</p>
<pre><code class="language-shell">./nginx -s reload
</code></pre>
<p>即可<br/>
<img src="media/15402950858118/15402954645283.jpg" alt=""/></p></li>
</ul>

<p>方法二：查找当前nginx进程号，然后输入命令：</p>

<pre><code class="language-shell">kill -HUP 进程号 
</code></pre>

<p>实现重启nginx服务<br/>
<img src="media/15402950858118/15402954740748.jpg" alt=""/></p>


		</div>

		

	</article>
 
	<article>
		 <header>
		  	<h1 class="entry-title"><a href="15397707900555.html">Markdown简明语法教程</a></h1>
			<p class="meta"><time datetime="2018-10-17T18:06:30+08:00" 
			pubdate data-updated="true">2018/10/17</time></p>
		 </header>
	  	<div class="entry-content">
		  	
		  	<ul>
<li>
<a href="#toc_0">基本</a>
</li>
<li>
<a href="#toc_1">字体</a>
</li>
<li>
<a href="#toc_2">换行</a>
</li>
<li>
<a href="#toc_3">标题</a>
</li>
</ul>
</li>
<li>
<a href="#toc_5">列表</a>
</li>
<li>
<a href="#toc_6">引用</a>
</li>
<li>
<a href="#toc_7">代码区块</a>
</li>
<li>
<a href="#toc_8">链接</a>
</li>
<li>
<a href="#toc_9">图片</a>
</li>
<li>
<a href="#toc_10">分隔线</a>
</li>
<li>
<a href="#toc_11">表格</a>
</li>
<li>
<a href="#toc_12">特殊符号</a>
</li>
<li>
<a href="#toc_13">推荐阅读</a>
</li>
</ul>


<p><strong>说明：</strong></p>

<ul>
<li>本教程基于Markdown语言编写</li>
<li>本教程定位为基础教程，更加详细的用法可参考其他资料。</li>
<li>本教程中代码块内的代码为Markdown的语法。</li>
<li>本教程中部分语法使用的是<a href="https://help.github.com/articles/github-flavored-markdown/">GitHub Flavored Markdown</a>，GFM语法与标准语法在不同环境中存在解析差异，本教程在使用时会加以说明。</li>
<li>推荐使用Haroopad(Win)、MarkdownPad(Win)和Mou(OS X)编辑器，部分编辑器中文效果欠佳，可通过自定义CSS字体解决。</li>
</ul>

<h2 id="toc_0">基本</h2>

<ul>
<li>Markdown是一种用来写作的轻量级标记语言。</li>
<li>用标记语法，来代替常见的排版格式。</li>
<li>兼容 HTML代码。</li>
<li>特殊字符自动转换，例如<code>&lt;</code>和<code>&amp;</code>。</li>
</ul>

<h2 id="toc_1">字体</h2>

<ul>
<li><p>使用星号<code>*</code>和底号<code>_</code>表示<code>&lt;em&gt;</code>标签。</p>
<p>例如：</p>
<pre><code class="language-text">*斜体*
_斜体_
</code></pre>
<p>效果：</p>
<p><em>斜体</em></p></li>
<li><p>使用双星号<code>**</code>和双底号<code>__</code>表示<code>&lt;strong&gt;</code>标签。</p>
<p>例如：</p>
<pre><code class="language-text">**强调**
__强调__
</code></pre>
<p>效果：</p>
<p><strong>强调</strong></p></li>
</ul>

<h2 id="toc_2">换行</h2>

<ul>
<li>单一段落用空白行。</li>
</ul>

<h2 id="toc_3">标题</h2>

<ul>
<li>生成<code>&lt;h1&gt;</code>-<code>&lt;h6&gt;</code>标签，是通过在文字前面加上同等个数<code>#</code>符号来实现。</li>
<li><p>出于美观，也可以使用对称的闭合式标题符号。</p>
<p>例如：</p>
<pre><code class="language-text">### 这是标题
### 这是标题 ###
</code></pre>
<p>效果：</p>
<h3 id="toc_4">这是标题</h3></li>
</ul>

<h2 id="toc_5">列表</h2>

<ul>
<li><p><code>*</code>，<code>-</code>，<code>+</code>这三个符号效果都一样，这3个符号被称为Markdown列表符号。而有序列表则使用数字接着一个英文句点（数字大小并不会影响输出序列）。</p>
<p>例如：</p>
<pre><code class="language-text">* 第一行
* 第二行<br/>
* 第三行<br/>
6. 第四行<br/>
5. 第五行<br/>
4. 第六行
</code></pre>
<p>效果：</p>
<ul>
<li>第一行</li>
<li>第二行</li>
<li>第三行</li>
<li>第四行</li>
<li>第五行</li>
<li>第六行</li>
</ul></li>
</ul>

<h2 id="toc_6">引用</h2>

<ul>
<li><code>&gt;</code>符号表示引用，可简写于第一行，也可以每一行都添加。</li>
<li>区块的引用可以嵌套，只需要在层次数上加上同等数量的<code>&gt;</code>符号。</li>
<li><p>引用内可以使用其他Markdown语法，包括标题、列表、代码区块等。</p>
<p>例如：</p>
<pre><code class="language-text">&gt;    引用
&gt;    &gt;    引用中的引用
</code></pre>
<p>效果：</p>
<blockquote>
<p>引用</p>
<blockquote>
<p>引用中的引用</p>
</blockquote>
</blockquote></li>
</ul>

<h2 id="toc_7">代码区块</h2>

<ul>
<li><p><code>`</code>是表示inline代码，4个<code> </code>（空格）来表示缩进式代码段，分别对应HTML的<code>&lt;code&gt;</code>，<code>&lt;pre&gt;</code>标签。也可以使用<code>```</code>来表达围栏式代码块（<strong>GFM语法</strong>，部分编辑器不支持），并指定他的语言类型，实现语法高亮。围栏式代码块可以大量减少缩进的使用，大规模的代码块使用非常方便。</p>
<p>例如：</p>
<pre><code class="language-text">`sort()` 函数按升序对给定数组的值排序。
</code></pre>
<p>普通的缩进式代码块。</p>
<pre><code class="language-text">  &lt;?php
      $my_array = array(&#39;a&#39; =&gt; &#39;Dog&#39;, &#39;b&#39; =&gt; &#39;Cat&#39;);<br/>
      sort($my_array);<br/>
      print_r($my_array);<br/>
  ?&gt;
</code></pre>
<p>带语法高亮的围栏式代码块（<strong>GFM语法</strong>，部分编辑器不支持）。</p>
<pre><code class="language-php">  &lt;?php
      $my_array = array(&#39;a&#39; =&gt; &#39;Dog&#39;, &#39;b&#39; =&gt; &#39;Cat&#39;);<br/>
      sort($my_array);<br/>
      print_r($my_array);<br/>
  ?&gt;
</code></pre>
<p>效果：</p>
<p><code>sort()</code> 函数按升序对给定数组的值排序。</p>
<p>普通的缩进式代码块。</p>
<p>&lt;?php<br/>
      \(my_array = array(&#39;a&#39; =&gt; &#39;Dog&#39;, &#39;b&#39; =&gt; &#39;Cat&#39;);<br/>
      sort(\)my_array);<br/>
      print_r($my_array);<br/>
  ?&gt;</p>
<p>带语法高亮的围栏式代码块（<strong>GFM语法</strong>，部分编辑器不支持）。</p>
<pre><code class="language-php">&lt;?php
  $my_array = array(&#39;a&#39; =&gt; &#39;Dog&#39;, &#39;b&#39; =&gt; &#39;Cat&#39;);<br/>
  sort($my_array);<br/>
  print_r($my_array);<br/>
?&gt;
</code></pre></li>
</ul>

<h2 id="toc_8">链接</h2>

<ul>
<li><p>Markdown支持两种形式的链接语法：行内式和参考式两种形式。</p>
<p>行内式链接，是在方括号后面接圆括号即可。<br/>
例如：</p>
<pre><code class="language-text">[google](http://google.com &quot;google&quot;)
</code></pre>
<p>效果：</p></li>
</ul>

<p><a href="http://google.com" title="google">google</a></p>

<h2 id="toc_9">图片</h2>

<ul>
<li>Markdown使用一种和链接很相似的语法来标记图片，只是多了一个<code>!</code>在最前面，同样也允许两种样式：行内式和参考式。</li>
<li><p>目前为止，Markdown还没有办法指定图片的宽高，如果你需要的话，你可以使用普通的<code>&lt;img&gt;</code>标签。</p>
<p>行内式链接，是在方括号后面接圆括号即可。<br/>
例如：</p>
<pre><code class="language-text">![image.png](http://lzh-images.oss-cn-shenzhen.aliyuncs.com/2c920872669f66b20166a0dec9a40003.png)
</code></pre>
<p>效果：</p>
<p><img src="http://lzh-images.oss-cn-shenzhen.aliyuncs.com/2c920872669f66b20166a0dec9a40003.png" alt="image.png"/></p></li>
</ul>

<h2 id="toc_10">分隔线</h2>

<ul>
<li><p>使用三个以上的<code>*</code>、<code>-</code>来建立一个分隔线，行内不能有其他字符。</p>
<p>例如：<br/>
<code>***</code></p>
<p>效果：</p></li>
</ul>

<hr/>

<h2 id="toc_11">表格</h2>

<ul>
<li><p>Markdown使用<code>|</code>和<code>-</code>来绘制表格，<code>:</code>可控制左对齐、右对齐及居中。</p>
<p>例如：</p>
<pre><code class="language-text">| Title   | Description                        |
| :------ | :--------------------------------: |<br/>
| Version | 0.0.1                              |<br/>
| Editor  | [lucas](mailto:lucas.shao@foxmail.com) |
</code></pre>
<p>效果：</p>
<table>
<thead>
<tr>
<th style="text-align: left">Title</th>
<th style="text-align: center">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left">Version</td>
<td style="text-align: center">0.0.1</td>
</tr>
<tr>
<td style="text-align: left">Editor</td>
<td style="text-align: center"><a href="mailto:lucas.shao@foxmail.com">lucas</a></td>
</tr>
</tbody>
</table></li>
</ul>

<h2 id="toc_12">特殊符号</h2>

<ul>
<li>Markdown利用<code>\</code>字符来转义一些在语法中有特殊意义的符号。</li>
</ul>

<h2 id="toc_13">推荐阅读</h2>

<ul>
<li><a href="http://wowubuntu.com/markdown/index.html">Markdown语法说明（简体中文版）</a></li>
<li><a href="http://daringfireball.net/projects/markdown/syntax">Markdown Syntax Documentation</a></li>
<li><a href="https://help.github.com/articles/github-flavored-markdown">GitHub Flavored Markdown</a></li>
</ul>


		</div>

		

	</article>
 
	<article>
		 <header>
		  	<h1 class="entry-title"><a href="15391742760274.html">XRebel 使用详解</a></h1>
			<p class="meta"><time datetime="2018-10-10T20:24:36+08:00" 
			pubdate data-updated="true">2018/10/10</time></p>
		 </header>
	  	<div class="entry-content">
		  	
		  	<ul>
<li>
<a href="#toc_0">前言</a>
<ul>
<li>
<a href="#toc_1">什么是XRebel</a>
</li>
<li>
<a href="#toc_2">如何下载XRebel</a>
</li>
<li>
<a href="#toc_3">如何安装XRebel</a>
</li>
<li>
<a href="#toc_4">如何使用</a>
</li>
</ul>
</li>
<li>
<a href="#toc_5">结语</a>
</li>
</ul>


<h2 id="toc_0">前言</h2>

<p>你有没有遇到过项目打开, 一直转圈的情况, 有没有遇到过追究责任前后端开发相互扯皮, 推卸责任的情况, 有没有遇到过项目性能不理想, 却手足无措的情况, 那么XRebel正是你需要的好东西.</p>

<h3 id="toc_1">什么是XRebel</h3>

<p>XRebel 是不间断运行在 web 应用的交互式分析器，当发现问题会在浏览器中显示警告信息。XRebel 会实时监测应用代码的性能指标和可能会发生的问题。</p>

<h3 id="toc_2">如何下载XRebel</h3>

<p>地址: <a href="http://zeroturnaround.com/software/xrebel/download/">http://zeroturnaround.com/software/xrebel/download/</a></p>

<h3 id="toc_3">如何安装XRebel</h3>

<p>只需要解压下载到本地的zip压缩文件即可, 解压后的文件夹内容如图:<br/>
<img src="media/15391742760274/15391749868448.jpg" alt=""/><br/>
注意其中的xrebel.jar<br/>
真正带来魔法的, 就是这个xrebel.jar文件</p>

<h3 id="toc_4">如何使用</h3>

<p>XRebel的使用, 使用的是java代理的方式, 实际是JVM代理, 只需要添加JVM参数即可, 这意味着, 不论你是什么样的java项目, 基于servlet+jsp的web应用也好, 还是jar包(例如: spring-boot项目)项目也好, 我们都是可以使用XRebel进行性能分析和问题监控的.</p>

<p>那么接下来我们以 IDEA + Spring-boot 项目为例, 来演示如何使用.</p>

<ul>
<li><p>首先配置一个Run-configuration:<br/>
<img src="media/15391742760274/15391752470216.jpg" alt=""/></p></li>
<li><p>然后在VM options: 中添加如下内容:<br/>
-javaagent:/Users/lucas/Java-lib/maven/xrebel/xrebel.jar<br/>
我们可以清楚的看到, 使用的是JVM代理, 后边要清楚的指明之前解压出来的xrebel.jar的位置.<br/>
如图:<br/>
<img src="media/15391742760274/15391753777547.jpg" alt=""/></p></li>
<li><p>随后我们运行起来项目, 只需要点击IDEA右上角的运行图标即可</p></li>
<li><p>在上一步骤的启动日志中, 可以看到XRebel UI的访问地址:<br/>
<img src="media/15391742760274/15391757236003.jpg" alt=""/><br/>
即: <a href="http://18.18.18.112:8080/xrebel">http://18.18.18.112:8080/xrebel</a></p></li>
<li><p>这时我们访问一个开启的服务的http服务接口, 这里以postman为例:<br/>
<img src="media/15391742760274/15391756059044.jpg" alt=""/></p></li>
<li><p>这时我们就可以在XRebel UI的页面, 看到详细的接口分析内容了:<br/>
<img src="media/15391742760274/15391758462364.jpg" alt=""/><br/>
可以看到里面详细的列出的请求执行的堆栈信息, 以及每一步的执行时间, 还有每个sql的执行时间.<br/>
通过这些信息足够我们做性能分析了.</p></li>
<li><p>尤其是sql语句的完整输出, 以及执行时间的详细信息, 能给我们关注这个性能点, 带来很大的方便:<br/>
<img src="media/15391742760274/15391760173058.jpg" alt=""/></p></li>
</ul>

<h2 id="toc_5">结语</h2>

<p>以上就是本次分享的内容, 是不是已经迫不及待想要尝试一把了~</p>


		</div>

		

	</article>
 
	<article>
		 <header>
		  	<h1 class="entry-title"><a href="15390848111879.html">koa-cache-control 中间件使用详解</a></h1>
			<p class="meta"><time datetime="2018-10-09T19:33:31+08:00" 
			pubdate data-updated="true">2018/10/9</time></p>
		 </header>
	  	<div class="entry-content">
		  	
		  	<ul>
<li>
<a href="#toc_0">什么是koa-cache-control</a>
</li>
<li>
<a href="#toc_1">为什么使用koa-cache-control</a>
</li>
<li>
<a href="#toc_2">如何安装</a>
</li>
<li>
<a href="#toc_3">使用示例</a>
</li>
<li>
<a href="#toc_4">现实demo</a>
</li>
</ul>


<h2 id="toc_0">什么是koa-cache-control</h2>

<blockquote>
<p>A simple method for managing cache control headers from your application. It also tries to provide a simple set of rules for common use cases such as setting &#39;max-age=0&#39; when &#39;no-cache&#39; is present by default.<br/>
中文: 提供简单的方式来管理 cache control 头信息, 也提供了几个简单的配置规则, 例如 当指定 &#39;no-cache&#39; 的时候设置&#39;max-age=0&#39;.</p>
</blockquote>

<h2 id="toc_1">为什么使用koa-cache-control</h2>

<p>要了解为什么要使用cache-control我们要先了解什么是 cache<br/>
具体可以参考: </p>

<ul>
<li><a href="https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn">https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn</a></li>
<li><a href="https://segmentfault.com/a/1190000005704489?utm_source=caibaojian.com">https://segmentfault.com/a/1190000005704489?utm_source=caibaojian.com</a></li>
</ul>

<p>接下来我们说为什么koa-cache-control中间件, 原因很简单, 因为这样更简单, 更高效, 更方便.</p>

<h2 id="toc_2">如何安装</h2>

<p><code>$ npm install koa-cache-control</code></p>

<h2 id="toc_3">使用示例</h2>

<pre><code class="language-javascript">const Koa = require(&#39;koa&#39;)
const morgan = require(&#39;koa-morgan&#39;)
const path = require(&#39;path&#39;);
const static = require(&#39;koa-static&#39;)
const fs = require(&#39;fs&#39;)
var cacheControl = require(&#39;koa-cache-control&#39;);

const app = new Koa();

app.use(cacheControl({
  maxAge: 86400
}));

// logger
app.use(morgan(&#39;:remote-addr - :remote-user [:date[clf]] &quot;:method :url HTTP/:http-version&quot; :status :res[content-length] :response-time ms&#39;));

// static assets
app.use(static(path.resolve(__dirname, &#39;./dist&#39;)));

//异步读取文件的形式
app.use(async (ctx, next) =&gt; {
  ctx.type = &#39;html&#39;;
  ctx.body = await fs.createReadStream(path.resolve(__dirname, &#39;./dist/index.html&#39;));
});

module.exports = app;
</code></pre>

<h2 id="toc_4">现实demo</h2>

<p>我们线上的知识库测试服务器解决缓存加载问题, 的就是 koa-cache-control 来支持的<br/>
访问地址: <a href="http://120.79.79.6:9000/">http://120.79.79.6:9000/</a></p>


		</div>

		

	</article>
 
	<article>
		 <header>
		  	<h1 class="entry-title"><a href="15390839723979.html">koa-static 中间件使用详解</a></h1>
			<p class="meta"><time datetime="2018-10-09T19:19:32+08:00" 
			pubdate data-updated="true">2018/10/9</time></p>
		 </header>
	  	<div class="entry-content">
		  	
		  	<ul>
<li>
<a href="#toc_0">什么是koa-static</a>
</li>
<li>
<a href="#toc_1">为什么使用koa-static</a>
</li>
<li>
<a href="#toc_2">如何安装</a>
</li>
<li>
<a href="#toc_3">使用示例</a>
</li>
<li>
<a href="#toc_4">现实demo</a>
</li>
</ul>


<h2 id="toc_0">什么是koa-static</h2>

<blockquote>
<p>Koa static file serving middleware, wrapper for koa-send.<br/>
中文: koa static 是一个文件服务中间件, 可以提供静态资源文件的访问支持, 基于 koa-send来实现.</p>
</blockquote>

<h2 id="toc_1">为什么使用koa-static</h2>

<blockquote>
<p>因为我们如果想要提供静态资源, 直接使用 koa 是不支持的, 需要编程来实现, 具体如何实现可以参考: <a href="https://chenshenhai.github.io/koa2-note/note/static/server.html">https://chenshenhai.github.io/koa2-note/note/static/server.html</a><br/>
而更简便的方法是使用业界已经开发好的开源中间件 -&gt; koa-static</p>
</blockquote>

<h2 id="toc_2">如何安装</h2>

<p><code>$ npm install koa-static</code></p>

<h2 id="toc_3">使用示例</h2>

<pre><code class="language-javascript">const serve = require(&#39;koa-static&#39;);
const Koa = require(&#39;koa&#39;);
const app = new Koa();
 
// $ GET /package.json
app.use(serve(&#39;.&#39;));
 
// $ GET /hello.txt
app.use(serve(&#39;test/fixtures&#39;));
 
// or use absolute paths
app.use(serve(__dirname + &#39;/test/fixtures&#39;));
 
app.listen(3000);
 
console.log(&#39;listening on port 3000&#39;);
</code></pre>

<h2 id="toc_4">现实demo</h2>

<p>我们线上的知识库测试服务器使用的就是 koa-static 来支持的<br/>
访问地址: <a href="http://120.79.79.6:9000/">http://120.79.79.6:9000/</a></p>


		</div>

		

	</article>
 
	<article>
		 <header>
		  	<h1 class="entry-title"><a href="15389634104231.html">PM2使用详解</a></h1>
			<p class="meta"><time datetime="2018-10-08T09:50:10+08:00" 
			pubdate data-updated="true">2018/10/8</time></p>
		 </header>
	  	<div class="entry-content">
		  	
		  	<ul>
<li>
<a href="#toc_0">什么是PM2</a>
</li>
<li>
<a href="#toc_1">为什么使用PM2</a>
</li>
<li>
<a href="#toc_2">PM2的主要特性</a>
</li>
<li>
<a href="#toc_3">如何安装</a>
</li>
<li>
<a href="#toc_4">如何部署应用</a>
</li>
<li>
<a href="#toc_5">设置pm2开机启动</a>
</li>
<li>
<a href="#toc_6">调整pm2运行的应用后, 保存到开机启动</a>
</li>
<li>
<a href="#toc_7">其他常用命令(以知识库项目为例)</a>
</li>
<li>
<a href="#toc_8">使用效果示例</a>
</li>
</ul>


<h2 id="toc_0">什么是PM2</h2>

<blockquote>
<p>pm2是一个进程管理工具,可以用它来管理你的node进程，并查看node进程的状态，当然也支持性能监控，进程守护，负载均衡等功能。</p>
</blockquote>

<h2 id="toc_1">为什么使用PM2</h2>

<blockquote>
<p>如果直接通过node app来启动，如果报错了可能直接停在整个运行，supervisor感觉只是拿来用作开发环境的。再网上找到pm2.目前似乎最常见的线上部署nodejs项目的有forever,pm2这两种,今天我们就来讲解PM2的使用</p>
</blockquote>

<h2 id="toc_2">PM2的主要特性</h2>

<ol>
<li>内建负载均衡（使用Node cluster 集群模块）</li>
<li>后台运行</li>
<li>0秒停机重载，我理解大概意思是维护升级的时候不需要停机.</li>
<li>具有Ubuntu和CentOS 的启动脚本</li>
<li>停止不稳定的进程（避免无限循环）</li>
<li>控制台检测</li>
<li>提供 HTTP API</li>
<li>远程控制和实时的接口API ( Nodejs 模块,允许和PM2进程管理器交互 )</li>
</ol>

<h2 id="toc_3">如何安装</h2>

<p><code>npm install -g pm2</code></p>

<h2 id="toc_4">如何部署应用</h2>

<p><code>pm2 start server.js --name=knowledge -i max</code></p>

<h2 id="toc_5">设置pm2开机启动</h2>

<p>运行 </p>

<pre><code class="language-shell">pm2 startup
</code></pre>

<p>即在/etc/init.d/目录下生成pm2-root的启动脚本，且自动将pm2-root设为服务。</p>

<h2 id="toc_6">调整pm2运行的应用后, 保存到开机启动</h2>

<p>运行</p>

<pre><code class="language-shell">pm2 save
</code></pre>

<p>会将当前pm2所运行的应用保存在/root/.pm2/dump.pm2下，当开机重启时，运行pm2-root服务脚本，并且到/root/.pm2/dump.pm2下读取应用并启动。</p>

<h2 id="toc_7">其他常用命令(以知识库项目为例)</h2>

<p><img src="media/15389634104231/15389967084289.jpg" alt=""/></p>

<h2 id="toc_8">使用效果示例</h2>

<p><img src="media/15389634104231/15390839430311.jpg" alt=""/></p>


		</div>

		

	</article>
  
	<div class="pagination">
	
<a href="archives.html">Blog Archives</a>
	 
	    
	</div>
</div>
 <aside class="sidebar"> 

	<section>
	  <h1>Categories</h1>
	  <ul id="recent_posts">
	  
	      <li class="post">
	        <a href="version-control.html"><strong>version-control&nbsp;(2)</strong></a>
	        
	        
	        
	      </li>
	  
	      <li class="post">
	        <a href="shell.html"><strong>shell&nbsp;(1)</strong></a>
	        
	        
	        
	      </li>
	  
	      <li class="post">
	        <a href="back-end.html"><strong>back-end&nbsp;(4)</strong></a>
	        
	        
	        
	      </li>
	  
	      <li class="post">
	        <a href="front-end.html"><strong>front-end&nbsp;(5)</strong></a>
	        
	        
	        
	      </li>
	  
	      <li class="post">
	        <a href="version-control-1.html"><strong>version-control&nbsp;(1)</strong></a>
	        
	        
	        
	      </li>
	  
	      <li class="post">
	        <a href="other.html"><strong>other&nbsp;(2)</strong></a>
	        
	        
	        
	      </li>
	   
	  </ul>
	</section>
	<section>
	  <h1>Recent Posts</h1>
	  <ul id="recent_posts">
	  
	      
		      <li class="post">
		        <a href="15413898294420.html">RESTful API 最佳实践</a>
		      </li>
	     
	  
	      
		      <li class="post">
		        <a href="15411425055347.html">LZH-DEV-GROUP Git Workflow 实践详解</a>
		      </li>
	     
	  
	      
		      <li class="post">
		        <a href="15410718240579.html">如何使用PM2进程管理器, 守护java进程</a>
		      </li>
	     
	  
	      
		      <li class="post">
		        <a href="15408811446857.html">一篇文章搞定 centos7 上的 mysql 安装</a>
		      </li>
	     
	  
	      
		      <li class="post">
		        <a href="15408703788547.html">一篇文章搞定 centos7 下安装 nvm和node</a>
		      </li>
	     
	  
	      
	  
	      
	  
	      
	  
	      
	  
	      
	  
	      
	  
	      
	  
	      
	  
	      
	  
	      
	   
	  </ul>
	</section>
	
</aside> </div></div>
  <footer role="contentinfo"><p>
  Copyright &copy; 2014 -  -
  <span class="credit">Powered by <a target="_blank" href="http://www.mweb.im">MWeb</a> &nbsp;&nbsp; Theme by <a href="http://octopress.org">Octopress</a></span>
</p>

</footer>

  
    




</body>
</html>